/*
 * GGoalsAccount.java
 * Created on 1 juil. 2007
 *
 * Glacéo Internet Platform
 * http://sourceforge.net/projects/chm/
 *
 * Copyright (c) 2005-2008, Jens and Jan Janke (VirtualHockey Project)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hockeo.vhbase.data.dao.standings;


import java.math.BigDecimal;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import com.hockeo.vhbase.conf.GRulesConstants;
import com.hockeo.vhbase.data.dao.IGEntity;
import com.hockeo.vhbase.data.dao.club.GClub;
import com.hockeo.vhbase.data.dao.contest.GContestElement;

/**
 * Indicates the amount of still available goals for a specific club/contest element
 * combination.
 *
 * @version $Id$
 * @author jjanke
 */
@Entity
@Table( name = "s_goals_account" )
public class GGoalsAccount implements IGEntity
{
  @Id
  @Column( name = "id" )
  @GeneratedValue( strategy = GenerationType.AUTO )
  private int             d_nId;

  @ManyToOne( fetch = FetchType.LAZY )
  @JoinColumn( name = "club_id" )
  private GClub           d_club;

  @ManyToOne( fetch = FetchType.LAZY )
  @JoinColumn( name = "contest_element_id" )
  private GContestElement d_contestElement;

  @Column( name = "goals_current" )
  private BigDecimal      d_decGoals;

  /**
   * The amount of goals that was initially available (before the first match was
   * substracted) on this goals account.
   */
  @Column( name = "goals_initial" )
  private BigDecimal      d_decInitialGoals;

  /** Default no argument constructor. */
  protected GGoalsAccount()
  {}

  /**
   * Constructs a new GoalsAccount.
   *
   * @param club the club for which to create the goals account
   * @param contestElement the contest element to which the goals account applies
   * @param decInitialGoals the initial number of goals on the goals account
   */
  public GGoalsAccount( GClub club, GContestElement contestElement, BigDecimal decInitialGoals )
  {
    d_club = club;
    d_contestElement = contestElement;
    d_decGoals = decInitialGoals;
    d_decInitialGoals = d_decGoals;
  }

  /**
   * Creates a new goals account by copying all values from the given previous goal
   * account. Only the contest element is not taken from the previous goal account but
   * from the especially provided value.
   *
   * @param element the new contest element to be used for the created goal account
   * @param gaPrev the previous goal account serving as pattern
   */
  public GGoalsAccount( GContestElement element, GGoalsAccount gaPrev )
  {
    d_club = gaPrev.getClub();
    d_contestElement = element;
    d_decGoals = gaPrev.getGoals();
    d_decInitialGoals = d_decGoals;
  }

  public GClub getClub()
  {
    return d_club;
  }

  public GContestElement getContestElement()
  {
    return d_contestElement;
  }

  public BigDecimal getGoals()
  {
    return d_decGoals;
  }

  /**
   * Resets the amount of currently available goals to the initial value used when
   * creating the goals account before the first match was played.
   */
  public void reset()
  {
    d_decGoals = d_decInitialGoals;
  }

  /**
   * Synchronizes the amount of goals available on this account with the amount of goals
   * available according to the given standings object.
   *
   * @param standings the standings with which to synchronize
   */
  public void syncWithStandings( GStandings standings )
  {
    if ( !d_contestElement.equals( standings.getMatchday().getContestElement() ) )
      throw new IllegalArgumentException( "The standings object refers to contest element '"
          + standings.getMatchday().getContestElement().getId() + "' whereas this goals account is for '"
          + d_contestElement.getId() + "'." );

    if ( !d_club.equals( standings.getClub() ) )
      throw new IllegalArgumentException( "The standings object refers to the club '" + standings.getClub().getId()
          + "' whereas this goals account is for '" + d_club.getId() + "'." );

    d_decGoals = standings.getRemainingGoals();
  }

  /**
   * Adds the amount of goals given as a parameter to this account.
   *
   * @param decGoals the number of goals to be added.
   * @param fChangeInitialAmount indicates whether the initial goal account amount should
   *          also be adjusted (if <code>true</code>, an {@link IllegalStateException} is
   *          thrown if the current and the initial amount are not identical)
   * @throws IllegalStateException if the number of goals on this account is negative
   *           after the subtraction
   */
  public void add( BigDecimal decGoals, boolean fChangeInitialAmount )
  {
    if ( fChangeInitialAmount && d_decGoals.compareTo( d_decInitialGoals ) != 0 )
      throw new IllegalStateException( "The goal account " + getId()
          + " cannot be modified because the initial amount of goals and the current amount are not equal." );

    d_decGoals = d_decGoals.add( decGoals );
    d_decInitialGoals = d_decGoals;

    if ( d_decGoals.signum() < 0 )
      throw new IllegalStateException( "The amount on the goals account with id " + getId() + " is negative." );
  }

  /**
   * Reduces the amount of goals given as a parameter from this account.
   *
   * @param decGoals the number of goals to be subtracted.
   * @throws IllegalStateException if the number of goals on this account is negative
   *           after the subtraction
   */
  public void subtract( BigDecimal decGoals )
  {
    d_decGoals = d_decGoals.subtract( decGoals );

    if ( d_decGoals.signum() < 0 )
      throw new IllegalStateException( "The amount on the goals account with id " + getId() + " is negative." );
  }

  /**
   * Sets the amount of available goals on this account to zero.
   *
   * The method is intentionally package visible only!
   *
   * @return returns the amount of goals which have been previously on this account
   *         (before it was set to 0)
   */
  public BigDecimal setToZero()
  {
    BigDecimal decTmp = d_decGoals;

    d_decGoals = GRulesConstants.ZERO_GOALS;

    return decTmp;
  }

  public int getId()
  {
    return d_nId;
  }

  public Class<? extends IGEntity> getGEntityClass()
  {
    return GGoalsAccount.class;
  }

  public boolean instanceOf( Class<? extends IGEntity> classOther )
  {
    return classOther.isAssignableFrom( getGEntityClass() );
  }

  /*
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals( Object obj )
  {
    try
    {
      return d_nId == ( (GGoalsAccount) obj ).getId();
    }
    catch ( Exception ex ) // especially ClassCastException(s) may happen
    {
      return false;
    }
  }

  /*
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode()
  {
    return d_nId;
  }

  /*
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString()
  {
    return "Goals account " + d_nId + " for " + d_club.getName() + " (contest element: " + d_contestElement.getId()
        + ")";
  }
}
